package main.java.exercise;

import main.java.framework.StudentInformation;
import main.java.framework.StudentSolution;

public class StudentSolutionImplementation implements StudentSolution {
    @Override
    public StudentInformation provideStudentInformation() {
        return new StudentInformation(
                "", // Vorname
                "", // Nachname
                "" // Matrikelnummer
        );
    }

    public void getNeighbors(int[] state, int[][] neighbors) {
        if (neighbors.length == 0)
            return;

        // track offset because of gaps in solution when boundary conditions
        // occur
        int offset = 0;
        // TODO: perhaps change ugly dualistic code
        for (int i = 0; i < state.length * 2; i++) {
            // integer division simulates the floor of i / 2, because each
            // queen possibly needs to be considered twice, making it necessary
            // to have a progression in the form of (0, 0, 1, 1, 2, 2, ...)
            int queen = i / 2;
            // every second iteration, move queen down instead of up
            if (i % 2 == 0) {
                // boundary check
                if (state[queen] == 0) {
                    ++offset;
                    continue;
                }
                // copy state into neighbours
                System.arraycopy(state, 0, neighbors[i - offset], 0, state.length);
                // move queen down
                neighbors[i - offset][queen] -= 1;
            } else {
                // boundary check
                if (state[queen] == state.length - 1) {
                    ++offset;
                    continue;
                }
                // copy state into neighbours
                System.arraycopy(state, 0, neighbors[i - offset], 0, state.length);
                // move queen up
                neighbors[i - offset][queen] += 1;
            }
        }
    }

    public int calculateNumberOfConflicts(int[] state) {
        int conflicts = 0;
        // queens are guaranteed to be alone in their column, thus requiring
        // checks only per row and diagonal
        int[] totalQueensPerRow = new int[state.length];
        int[] totalQueensPerDi1 = new int[state.length + state.length - 1];
        int[] totalQueensPerDi2 = new int[state.length + state.length - 1];

        for (int i = 0; i < state.length; i++) {
            // add queens for row
            totalQueensPerRow[state[i]] += 1;
            // add queens for di1
            totalQueensPerDi1[state[i] + i] += 1;
            // add queens for di2
            totalQueensPerDi2[state.length - 1 + state[i] - i] += 1;
        }
        // count conflicting pairs using summation formula
        // for number of edges in complete graph
        for (int value : totalQueensPerRow)
            conflicts += ((value  * (value - 1)) / 2);
        for (int value : totalQueensPerDi1)
            conflicts += ((value  * (value - 1)) / 2);
        for (int value : totalQueensPerDi2)
            conflicts += ((value  * (value - 1)) / 2);

        return conflicts;
    }

    public int indexOfBestImprovement(int numberOfConflictsInState, int[] numberOfConflictsInNeighbors) {
        int newLow = numberOfConflictsInState;
        int index = -1;

        // iterate all neighbouring solutions
        for (int i = 0; i < numberOfConflictsInNeighbors.length; i++) {
            int conflicts = numberOfConflictsInNeighbors[i];
            // update index and newLow if neighbouring solution is better than
            // current best
            index = conflicts < newLow ? i : index;
            newLow = conflicts < newLow ? conflicts : newLow;
        }

        return index;
    }

}
